package com.lihan.netty;

import com.lihan.SpringUtil;
import com.lihan.enums.MsgActionEnum;
import com.lihan.services.IChatService;
import com.lihan.services.IUserService;
import com.lihan.utils.JsonUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.codehaus.plexus.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * ClassName: ChatHandler
 * Package: com.lihan.websocket
 * Description:
 *  自定义Handler
 * @Author 李涵
 * @Create 2025-02-27 20:10
 * @Version 1.0
 */
/*
* 用于处理消息的自定义handler
* TextWebSocketFrame： 在netty中，是用于为websocket专门处理文本的对象，frame是消息的载体
* */
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    private static final Logger log = LoggerFactory.getLogger(ChatHandler.class);


    //用于记录和管理所有客户端的所有channel
    public static ChannelGroup users  =  new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /*
    * channelRead0 对读的处理
    * channelHandlerContext 是上下文
    * textWebSocketFrame  消息的内容
    * */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        //获取客户端所传输的消息
        String content = msg.text();

        /*
        * 1.获取客户端发来的消息
        * 2.判断消息的类型，根据不同的类型来处理不同的业务
        * */
        DataContent dataContent = JsonUtils.jsonToPojo(content, DataContent.class);
        Integer action  = dataContent.getAction(); //获取消息的类型

        //获取通道
        Channel channel = ctx.channel();

        if(action == MsgActionEnum.CONNECT.getType()){

            //把 channel 和 userid 关联起来,就是哪个用户对应哪个连接
            String sendId = dataContent.getChatMsg().getSendId();
            UserChannelRel.put(sendId,channel);
            System.out.println("前端发送连接消息");

        }else if(action == MsgActionEnum.CHAT.getType()){
            //如果是进行聊天的消息，把聊天记录保存到数据库中，同时标记消息的签收状态（未签收）

            // 获取消息，保存到数据库，并且标注为未签收
            // handler中无法直接注入调用spring中的service，需要提供一个springUtil的工具类
            // 默认名是类名的首字母小写
            ChatMsg chatMsg = dataContent.getChatMsg();
            IChatService chatService = (IChatService) SpringUtil.getBean("IChatServiceImpl");
            String msgId = chatService.savaMsg(chatMsg);

            chatMsg.setMsgId(msgId);
            DataContent dataContentMsg = new DataContent();
            dataContentMsg.setChatMsg(chatMsg);

            //接收者的id
            String receiveId = chatMsg.getReceiveId();

            //实现发送消息，获取接收者的channel
            Channel receiverChannel = UserChannelRel.get(receiveId);

            if(receiverChannel != null){
                //当receiveChannel 不为空的时候，从ChannelGroup 查找对应的channel 是否存在
                Channel findChannel = users.find(receiverChannel.id());

                if(findChannel !=null){
                    //用户在线
                    //返回聊天数据到前端
                    receiverChannel.writeAndFlush(new TextWebSocketFrame(JsonUtils.objectToJson(dataContentMsg)));
                }
            }

        }else if(action == MsgActionEnum.SIGNED.getType()){
            //2.3 签收消息类型，针对具体的消息进行签收，修改数据库中对应消息的签收状态（已签收）
            //消息收到就算签收
            IChatService chatService = (IChatService) SpringUtil.getBean("IChatServiceImpl");

            //扩展字段在SIGNED 类型消息中，代表需要去签收的消息id，逗号间隔
            String msgIds = dataContent.getExtend();
            String[] msgsId = msgIds.split(",");

            List<String> msgIdList = new ArrayList<>();
            for (String mid:msgsId) {
                if(StringUtils.isNotBlank(mid)){
                    msgIdList.add(mid);
                }
            }
            System.out.println(msgIdList.toString());

            if(msgIdList != null && msgIdList.size()>0){
                //批量签收
                chatService.updateMsgSigned(msgIdList);
            }

        }else if(action == MsgActionEnum.KEEPALIVE.getType()){
            //2.4 心跳类型的消息
            System.out.println("收到来自channel为"+channel+"的心跳包");
        }
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        users.add(ctx.channel()); //当客户端连接到服务端之后，获取服务端的channel,并且放在ChannelGroup中进行管理
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        /*String channelId = ctx.channel().id().asShortText();
        System.out.println("客户端被移除 ：channel ID"+channelId);*/
        log.info("客户端被移除");
        users.remove(ctx.channel());
    }

    /***
     * @description exceptionCaught 捕获异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
       //打印异常
        cause.printStackTrace();
        //发生异常之后关闭连接，同时从channelGroup移出
        ctx.channel().close();
        users.remove(ctx.channel());
    }
}
